package objects;

import static objects.AdapletConstants.sNewLine;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import sections.FileSection;
import sections.ISection.Section;
import sections.InterfaceSection;
import sections.ReferenceTypeSection;
import sections.RelToRelSection;
import sections.RelationshipSection;
import sections.TableJoinSection;
import sections.TableSection;
import sections.TypeSection;
import util.ISectionListener;
import util.ListenerManager;

public class MappingFile implements Serializable
{
	/**
	 * 
	 */
	private static final long serialVersionUID = 7399046592779705935L;

	private String fMapFileName;

	private TableSection fTableSection;
	private TableJoinSection fTableJoinSection;
	private TypeSection fTypeSection;
	private ReferenceTypeSection fReferenceTypeSection;
	private RelationshipSection fRelationshipSection;
	private RelToRelSection fRelToRelSection;
	private InterfaceSection fInterfaceSection;
	private FileSection fFileSection;

	private String fSchema;
	private String fComments;

	private String fFieldDelimiter;
	private String fRecordDelimiter;

	private String fMode;

	private String fMaxNotFound;

	private String fTagDBPath;
	private String fTagAdminPath;

	private String fLicense;

	private Map<String, String> fAdditionalItems;
	
	private transient ListenerManager fListenerManager;

	public MappingFile()
	{
		setMapFileName("map.map");

		fTableSection = new TableSection();
		fTableJoinSection = new TableJoinSection();
		fTypeSection = new TypeSection();
		fReferenceTypeSection = new ReferenceTypeSection();
		fRelationshipSection = new RelationshipSection();
		fRelToRelSection = new RelToRelSection();
		fInterfaceSection = new InterfaceSection();
		fFileSection = new FileSection();

		fSchema = "";
		fComments = "";
		fFieldDelimiter = "1C";
		fRecordDelimiter = "1E";
		fMode = "readonly";
		fMaxNotFound = "";
		fTagDBPath = "";
		fTagAdminPath = "";
		fLicense = "";

		fAdditionalItems = new HashMap<String, String>();
		
		fListenerManager = new ListenerManager();
	}

	public MappingFile(String mapName)
	{
		setMapFileName(mapName);

		fTableSection = new TableSection();
		fTableJoinSection = new TableJoinSection();
		fTypeSection = new TypeSection();
		fReferenceTypeSection = new ReferenceTypeSection();
		fRelationshipSection = new RelationshipSection();
		fRelToRelSection = new RelToRelSection();
		fInterfaceSection = new InterfaceSection();

		fSchema = "";
		fComments = "";
		fFieldDelimiter = "";
		fRecordDelimiter = "";
		fMode = "";
		fMaxNotFound = "";
		fTagDBPath = "";
		fTagAdminPath = "";
		fLicense = "";

		fAdditionalItems = new HashMap<String, String>();
	}

	public TableSection getTableSection()
	{
		return fTableSection;
	}

	public TableJoinSection getTableJoinSection()
	{
		return fTableJoinSection;
	}

	public TypeSection getTypeSection()
	{
		return fTypeSection;
	}

	public ReferenceTypeSection getReferenceTypeSection()
	{
		return fReferenceTypeSection;
	}

	public RelationshipSection getRelationshipSection()
	{
		return fRelationshipSection;
	}

	public RelToRelSection getRelToRelSection()
	{
		return fRelToRelSection;
	}

	public InterfaceSection getInterfaceSection()
	{
		return fInterfaceSection;
	}
	
	public FileSection getFileSection()
	{
		return fFileSection;
	}

	public void setMapFileName(String mapFileName)
	{
		this.fMapFileName = mapFileName;
	}

	public String getMapFileName()
	{
		return fMapFileName;
	}

	public String getMapFileNameWithExt()
	{
		return fMapFileName + ".map";
	}

	public String buildMapFileDefinition()
	{
		StringBuffer mapFileBuf = new StringBuffer();

		mapFileBuf.append("");
		mapFileBuf.append(sNewLine);

		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# Filename:\t");
		mapFileBuf.append(fMapFileName);
		mapFileBuf.append(".map");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);

		mapFileBuf.append("# Function:     This MAP file defines the defintions for the ENOVIA schema.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#               The mappings use Oracle tables defined for the purpose.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# Usage:        To use this MAP file, it is necessary to create a database that");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#               uses the defined schema.  It is then necessary to create a");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#               corresponding set of tables as defined below.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(AdapletConstants.commentizeString(fComments));
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# Define the Matrix server/schema that will be used by this Adaptlet.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   schema  -  Specify the Oracle Schema defined within this Oracle instance.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   server  -  Specify the Matrix Server Object that connects to the foreign");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#              schema.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   Direct connection to database:");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#     item Username - Database user name where tables reside.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#     item Password - Database user password where tables reside.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#     item ConnectString - Database user connection string where tables reside.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);

		if (fSchema != null && !fSchema.isEmpty())
		{
			mapFileBuf.append("schema ");
			mapFileBuf.append(fSchema);
			mapFileBuf.append(";");
			mapFileBuf.append(sNewLine);
			mapFileBuf.append(sNewLine);
		}

		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# Define the field and record seperators that will be used by this Adaptlet.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# item ExportFieldDelimiter - Specify the field termination character.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# item ExportRecordDelimiter - Specify the record termination character.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);

		if (fFieldDelimiter != null && !fFieldDelimiter.isEmpty())
		{
			mapFileBuf.append("item ExportFieldDelimiter ");
			mapFileBuf.append(fFieldDelimiter);
			mapFileBuf.append(";");
			mapFileBuf.append(sNewLine);
		}

		if (fRecordDelimiter != null && !fRecordDelimiter.isEmpty())
		{
			mapFileBuf.append("item ExportRecordDelimiter ");
			mapFileBuf.append(fRecordDelimiter);
			mapFileBuf.append(";");
			mapFileBuf.append(sNewLine);
		}

		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# Define the Mode for this Adaptlet.  Valid modes are:");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   readonly    - Data can be viewed but not modified.");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   readwrite   - Data can be read and changed by both systems");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   extend      - Data can be written to foreign database and to Matrix");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#   migrate     - Data will migrate into Matrix tables when it is modified");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);

		mapFileBuf.append("mode ");
		if (fMode != null && !fMode.isEmpty())
		{
			mapFileBuf.append(fMode);
		}
		else
		{
			mapFileBuf.append("readonly");
		}
		mapFileBuf.append(";");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("# This area defines any additional items that setup the environment");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("################################################################################");
		mapFileBuf.append(sNewLine);
		mapFileBuf.append("#");
		mapFileBuf.append(sNewLine);

		if (fMaxNotFound != null && !fMaxNotFound.isEmpty())
		{
			mapFileBuf.append("item MaxNotFoundReport ");
			mapFileBuf.append(fMaxNotFound);
			mapFileBuf.append(";");
			mapFileBuf.append(sNewLine);
		}

		if (fTagDBPath != null && !fTagDBPath.isEmpty())
		{
			mapFileBuf.append("item \"ExportTagDBPath\" \"");
			mapFileBuf.append(fTagDBPath);
			mapFileBuf.append("\";");
			mapFileBuf.append(sNewLine);
		}

		if (fTagAdminPath != null && !fTagAdminPath.isEmpty())
		{
			mapFileBuf.append("item \"ExportAdminTagDBPath\" \"");
			mapFileBuf.append(fTagAdminPath);
			mapFileBuf.append("\";");
			mapFileBuf.append(sNewLine);
		}

		if (fLicense != null && !fLicense.isEmpty())
		{
			mapFileBuf.append("item LicenseKey \"");
			mapFileBuf.append(fLicense);
			mapFileBuf.append("\";");
			mapFileBuf.append(sNewLine);
		}

		for (String item : fAdditionalItems.keySet())
		{
			mapFileBuf.append("item \"");
			mapFileBuf.append(item);
			mapFileBuf.append("\" \"");
			mapFileBuf.append(fAdditionalItems.get(item));
			mapFileBuf.append("\";");
			mapFileBuf.append(sNewLine);
		}

		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fTableSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fTableJoinSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fTypeSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fReferenceTypeSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fRelationshipSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fRelToRelSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		mapFileBuf.append(fInterfaceSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);
		
		mapFileBuf.append(fFileSection.toString());
		mapFileBuf.append(sNewLine);
		mapFileBuf.append(sNewLine);

		return mapFileBuf.toString();
	}

	public void addAdditionalItem(String item, String value)
	{
		fAdditionalItems.put(item, value);
	}

	@Override
	public String toString()
	{
		return buildMapFileDefinition();
	}

	public void setComments(String text)
	{
		fComments = text;
	}

	public String getComments()
	{
		return fComments;
	}

	public void setLicense(String text)
	{
		fLicense = text;
	}

	public String getLicense()
	{
		return fLicense;
	}

	public void setMaxNotFound(String text)
	{
		fMaxNotFound = text;
	}

	public String getMaxNotFound()
	{
		return fMaxNotFound;
	}

	public void setTagAdmin(String text)
	{
		fTagAdminPath = text;
	}

	public String getTagAdmin()
	{
		return fTagAdminPath;
	}

	public void setTagDB(String text)
	{
		fTagDBPath = text;
	}

	public String getTagDB()
	{
		return fTagDBPath;
	}

	public void setRecordDelimiter(String text)
	{
		fRecordDelimiter = text;
	}

	public String getRecordDelimiter()
	{
		return fRecordDelimiter;
	}

	public void setFieldDelimiter(String text)
	{
		fFieldDelimiter = text;
	}

	public String getFieldDelimiter()
	{
		return fFieldDelimiter;
	}

	public void setSchema(String text)
	{
		fSchema = text;
	}

	public String getSchema()
	{
		return fSchema;
	}

	public void setMode(String text)
	{
		fMode = text;
	}

	public String getMode()
	{
		return fMode;
	}

	private void readObject(ObjectInputStream iStream) throws IOException, ClassNotFoundException
	{
		iStream.defaultReadObject();
		
		if(fListenerManager == null)
		{
			fListenerManager = new ListenerManager();
		}
		
		validateState();
	}

	private void writeObject(ObjectOutputStream oStream) throws IOException
	{
		oStream.defaultWriteObject();
	}

	private void validateState()
	{
		if (fMapFileName == null || fMapFileName.isEmpty())
		{
			throw new IllegalArgumentException("");
		}
		else if (fTableSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fTableJoinSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fTypeSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fReferenceTypeSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fRelationshipSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fRelToRelSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fInterfaceSection == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fSchema == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fComments == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fFieldDelimiter == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fRecordDelimiter == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fMode == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fMaxNotFound == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fTagAdminPath == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fTagDBPath == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fLicense == null)
		{
			throw new IllegalArgumentException("");
		}
		else if (fAdditionalItems == null)
		{
			throw new IllegalArgumentException("");
		}
	}

	@Override
	public boolean equals(Object obj)
	{
		if (obj == null)
		{
			return false;
		}
		else
		{
			if (obj instanceof MappingFile)
			{
				return ((MappingFile) obj).getMapFileName().equals(fMapFileName);
			}
			else
			{
				return false;
			}
		}
	}

	public String[] getAdditionalItemNames()
	{
		return fAdditionalItems.keySet().toArray(new String[0]);
	}

	public String getAdditionalItemValue(String name)
	{
		return fAdditionalItems.get(name);
	}

	public void removeAdditionalItem(String item)
	{
		fAdditionalItems.remove(item);
	}

	public void removeObject(Section section, IObject iObject)
	{
		switch (section)
		{
		case INTERFACE:
			fInterfaceSection.removeInterface((Interface) iObject);
			break;
		case REF_TYPE:
			fReferenceTypeSection.removeReferenceType((ReferenceType) iObject);
			break;
		case REL:
			fRelationshipSection.removeRelationship((Relationship) iObject);
			break;
		case REL_TO_REL:
			fRelToRelSection.removeRelToRel((RelToRel) iObject);
			break;
		case TABLE:
			fTableSection.removeTable((Table) iObject);
			break;
		case TYPE:
			fTypeSection.removeType((Type) iObject);
			break;
		case TABLE_JOIN:
			fTableJoinSection.removeTableJoin((TableJoin) iObject);
			break;
		case FILE:
			fFileSection.removeFile((File) iObject);
			break;
		default:
			break;
		}

	}
	
	public void registerListener(Section section, ISectionListener listener)
	{
		fListenerManager.registerListener(section, listener);
	}
	
	public void unregisterListener(Section section, ISectionListener listener)
	{
		fListenerManager.unregisterListener(section, listener);
	}
	
	public void notifyListeners(Section section)
	{
		fListenerManager.notifyListeners(section);
	}
	
//	public static String[] getObjectNames(IObject[] objects)
//	{
//		String[] names = new String[objects.length];
//		
//		for(int i = 0; i < names.length; i++)
//		{
//			names[i] = objects[i].getObjectName();
//		}
//		
//		return names;
//	}

}
